using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using Needle.Core.Injection;
using Needle.Core.Lifetime;
using Needle.Simulation;

namespace Needle.Simulation
{
    [Transient]
    public class Elevator : Process
    {

        public Elevator()
        {
        }

        private Buttons _buttons;
        [Inject]
        public Buttons Buttons
        {
            get { return _buttons; }
            set { _buttons = value; }
        }

        private int _CurrentFloor;
        [Inject("InitialFloor")]
        public int CurrentFloor
        {
            get { return _CurrentFloor; }
            set {
                if (_CurrentFloor != value)
                {
                    _CurrentFloor = value;
                    Log("Arrived at floor {0}.", value);
                }
            }
        }

        private bool _DoorsOpen;
        [Inject("InitialDoorState")]
        public bool DoorsOpen
        {
            get { return _DoorsOpen; }
            set
            {
                if (_DoorsOpen != value)
                {
                    _DoorsOpen = value;
                    if (value) Log("Doors opened."); else Log("Doors closed.");
                }
            }
        }

        private Direction _Direction;
        [Inject("InitialDirection")]
        public Direction Direction
        {
            get { return _Direction; }
            set
            {
                if (_Direction != value)
                {
                    _Direction = value;
                    Log("Changed direction to {0}", value);
                }
            }
        }

        protected override IEnumerable<Task> ProcessDefinition()
        {
            yield return MoveToFloor(0);
            yield return OpenDoors();
            while (true)
            {
                yield return WaitEvent("ButtonPressed");
                int floorNumber = GetNextFloorNumber();
                while (floorNumber != -1)
                {
                    if (floorNumber == CurrentFloor)
                    {
                        Buttons[CurrentFloor] = false;
                        break;
                    }
                    yield return CloseDoors();
                    yield return MoveToFloor(floorNumber);
                    yield return OpenDoors();
                    floorNumber = GetNextFloorNumber();
                }
            }
        }

        private Task OpenDoors()
        {
            return Delay(TimeSpan.FromSeconds(3), () =>  DoorsOpen = true );
        }

        private Task CloseDoors()
        {
            return  Delay(TimeSpan.FromSeconds(3), () => DoorsOpen = false );
        }

        private Task MoveToFloor(int floor)
        {
            int distance = Math.Abs(CurrentFloor - floor);
            int nextFloor = floor;
            return Delay(TimeSpan.FromSeconds(10 * distance), () =>
            {
                CurrentFloor = nextFloor;
                Buttons[CurrentFloor] = false;
            });
        }

        private int GetNextFloorNumber()
        {
            int floor = -1;
            switch (Direction)
            {
                case Direction.Up:
                    for (int i = 0; i < 32; i++)
                    {
                        if (Buttons[i])
                        {
                            floor = i;
                            if (i > CurrentFloor)
                                break;
                        }
                        if (i == 31)
                            Direction = Direction.Down;

                    }
                    break;
                case Direction.Down:
                    for (int i = 31; i >= 0; i--)
                    {
                        if (Buttons[i])
                        {
                            floor = i;
                            if (i < CurrentFloor)
                                break;
                        }
                        if (i == 0)
                            Direction = Direction.Up;

                    }
                    break;
            }
            return floor;

        }

    }
}
